#ifndef __PPPMERGE_H_
#define __PPPMERGE_H_

#include <Task.h>
#include <TFrame.h>
#include <pppnode.h>
#include <sys/time.h>

class pppmerge : public Task {private:
    Traverse <PPPWGraph> * mst; //!< pointer to traverse graph
    std::queue <int> * mst_q; //!< pointer to queue
    int * tcount; //!< pointer to count of working tasks
    pthread_mutex_t * tcount_mutex; //!< pointer to mutex on count
    pthread_mutex_t * qmutex; //!< pointer to queue mutex 
    int tovisit; //!< vertex id of vertex to visit from
    pthread_mutex_t * parentmutex; //!< pointer to single parent mutex
    std::vector <pthread_mutex_t * > * pmutex; //!< pointer to parent mutex
    std::vector <pthread_mutex_t * > * vmutex; //!< pointer to visited mutex
    WorkQueue * Q; //!< pointer to original work queue

    public:
    /**
     * \brief default constructor
     */
    pppmerge();

    /**
     * \brief constructor
     */
    pppmerge(Traverse <PPPWGraph> *,        //!< pointer to traverse
            std::queue <int> *,             //!< pointer to queue
            int *,                          //!< pointer to count of working tasks
            pthread_mutex_t *,              //!< pointer to count mutex
            pthread_mutex_t *,              //!< pointer to queue mutex
            pthread_mutex_t *,              //!< pointer to parent mutex  
            std::vector < pthread_mutex_t * > *,              //!< pointer to visited mutex
            std::vector < pthread_mutex_t * > *,              
            WorkQueue * _Q,                 //!< pointer to work queue
            int                             //!< vertex id to visit from
            );

    /**
     * \brief destructor
     */
    ~pppmerge();

    /**
     * \brief run method
     */
    virtual void run();

/*    virtual char* whoami(int& i) {
        i = id;
        return "pppMerge";
    }
    */
}; // end of pppmerge class

// default constructor
pppmerge::pppmerge() {
    mst          = NULL;
    mst_q        = NULL;
    tcount       = NULL;
    tcount_mutex = NULL;
    qmutex       = NULL;
    pmutex       = NULL;
    vmutex       = NULL;
    Q            = NULL;
} // end of constructor

// constructor
pppmerge::pppmerge(Traverse <PPPWGraph> * _mst, std::queue <int> * _q_mst, int * _tcount, pthread_mutex_t * _tcount_mutex, pthread_mutex_t * _qmutex,pthread_mutex_t * _parentmutex, std::vector< pthread_mutex_t * > * _vmutex, std::vector < pthread_mutex_t * > * _pmutex, WorkQueue * _Q, int _tovisit) {
    mst = _mst;
    mst_q = _q_mst;
    tcount = _tcount;
    tcount_mutex = _tcount_mutex;
    tovisit = _tovisit;
    parentmutex = _parentmutex;
    qmutex = _qmutex;
    pmutex = _pmutex;
    vmutex = _vmutex;
    Q = _Q;
} // end of constructor

// destructor
pppmerge::~pppmerge() { 
    mst          = NULL;
    mst_q        = NULL;
    tcount       = NULL;
    tcount_mutex = NULL;
    qmutex       = NULL;
    pmutex       = NULL;
    vmutex       = NULL;
    Q            = NULL;
} // end of destrcutor

// run method
void pppmerge::run() {
    std::vector<std::vector < PPPWEdge > * > & edges = mst->graph->edges;
    int neighbor_size = edges[tovisit]->size();
    std::vector <PPPWEdge>::iterator it, min_it;
    int mycount = -1; // track number of created tasks
    double t1, t2;
    timeval t;
    int to;
    int wMin = 1000;
    int eMin;
    for (it = edges[tovisit]->begin() ; it != edges[tovisit]->end() ; it++) {
        to = (*it).to;// get neighbor id
        pthread_mutex_lock(vmutex->at(to));
        if (mst->visited.getBit(to)) { // visisted
            pthread_mutex_unlock(vmutex->at(to));
            pthread_mutex_lock(pmutex->at(to));
            if (mst->parent[to] != tovisit && mst->parent[to] != -1) {
                int parent = mst->parent[to]; // get parent
                pthread_mutex_unlock(pmutex->at(to));
                // need to get the edge weight
                std::vector <PPPWEdge>::iterator _it;
                for (_it = edges[parent]->begin(); _it != edges[parent]->end() ; _it++) {
                    if ((*_it).to == to)  
                        break;
                }
                int myweight = (*it).w; 
                int parent_weight = (*_it).w;
                if (myweight < parent_weight) { // need to switch I have lower weight
                    pthread_mutex_lock(pmutex->at(to));
                    mst->parent[to] = tovisit; // updated the parent relation
                    pthread_mutex_unlock(pmutex->at(to));
                }
            } else
                pthread_mutex_unlock(pmutex->at(to));
        } else { // not visited
            // mark it as visited
            mst->visited.setBit(to);
            pthread_mutex_unlock(vmutex->at(to));
            // fix parent 
            pthread_mutex_lock(pmutex->at(to));
            mst->parent[to] = tovisit;
            pthread_mutex_unlock(pmutex->at(to));
            // add to queue
            pppmerge * task  = new pppmerge (mst, mst_q, tcount, tcount_mutex, qmutex, parentmutex, vmutex, pmutex, Q, to); 
            Q->addTask(task);
            mycount++;
            //pthread_mutex_lock(qmutex);
            //mst_q->push(to);
            //pthread_mutex_unlock(qmutex);
        }
    }
    // decrement count and declare to be done
    pthread_mutex_lock(tcount_mutex);
    (*tcount) += mycount;
    pthread_mutex_unlock(tcount_mutex);
    doneState = Done; //declared done
} // end of run method



#endif
